/*
|-----------------------------------------------------------------------------|
| Overall ANTLR Grammar File Structure                                        |
|-----------------------------------------------------------------------------|
| «grammarType» grammar name;
|   «optionsSpec»     = options { ... }
|   «tokensSpec»      = tokens { ... }
|   «attributeScopes» = «@action-scope-name::action-name» { ... }
|   «actions»
|   // rules
|   rule1 : ... ;
|   rule2 : ... ;
|   ...
|_____________________________________________________________________________|
|   «grammarType»         = ( lexer | parser | tree | <empty> )
|   «optionsSpec»         = options { ... }
|   «tokensSpec»          = tokens { ... }
|   «attributeScopes»     = 
|   «actions»             = @«action-scope-name»::«action-name» { ... }
|    «action-scope-name» = ( lexer | parser | tree | <empty> = parser )
|    «action-name»       = ( header | members | rulecatch | synpredgate )
|-----------------------------------------------------------------------------|
*/
/**
 * @asignatura: Procesadores de Lenguajes.
 * @autor:      Anthony Vittorio Russo Cabrera
 * @curso:      2012/2013
 * @práctica:   06 - ANTLR. Ambigüedades Sintácticas
 *
 * Referencias:
 *  - http://www.antlr.org/download/examples-v3.tar.gz
 *  |-> examples-v3/java/LL-star/SimpleC.g
 *  |-> examples-v3/java/C/C.g
 *    
 */
parser grammar CParser;

  options {
    language = Java;
    output = AST;
    tokenVocab = CLexer;
    backtrack=true;
    memoize=true;
    k=2;
  }// options

  scope Symbols {
    Set types; // only track types in order to get parser working
  }// scope

  @parser::header {
    package pl.p06.antlr3;
    import java.util.Set;
    import java.util.HashSet;
  }//header
  @parser::members {
  
    boolean isTypeName(String name) {
      for (int i = Symbols_stack.size()-1; i>=0; i--) {
        Symbols_scope scope = (Symbols_scope)Symbols_stack.get(i);
        if ( scope.types.contains(name) ) {
          return true;
        }// if
      }// for
      return false;
    }// isTypeName

  }// members

//= Grammar Rules C ==========================================================|
rulesC
scope Symbols; // entire file is a scope
@init {
  $Symbols::types = new HashSet();
}
  : (include)* (define)* (external_declaration)+
  ;

	include
	  : NumberSign Include 
	    ( Less Identifier ( Dot Identifier )? Greater
	    | StringLiteral ) 
	  ;

  define
	  : NumberSign Define Identifier constant
	  ;

  external_declaration
    : declaration
    | function_prototype
    | function_definition
    ;
//-----------------------------------------------------------------------------
// F u n c t i o n s

  function_prototype
    : type_specifier f1=function_header
      {System.out.println($f1.name + " es un prototipo");}
      (Comma fn=function_header)*
      {System.out.println($fn.name + " es un prototipo");}
      SemiColon
    ;

  function_definition
    : type_specifier function_header compound_statement
      {System.out.println($function_header.name + " es una definición");}
    ;

	  function_header returns [String name]
	  scope Symbols; // put parameters and locals into same scope for now
	  @init {
	    $Symbols::types = new HashSet();
	    $name = null; // for now you must init here rather than in 'returns'
	  }// init
      : Identifier LeftParenthesis ( parameter_type_list )? RightParenthesis
        { $name = $Identifier.text; }
	    ;

	    parameter_type_list
	      : parameter_list (Comma Ellipsis)?
	      | Void
	      ;

	      parameter_list
	        : parameter_declaration (Comma parameter_declaration)*
	        ;

	        parameter_declaration
		        : declaration_specifiers (declarator)*
		        ;

	function_invocation returns [String name]
	@init {
	  $name=null; // for now you must init here rather than in 'returns'
	}// init
	  : Identifier LeftParenthesis ( argument_expression_list )? RightParenthesis
	    { $name = $Identifier.text; }
	  ;
	
	  argument_expression_list
	    : conditional_expression (Comma conditional_expression)*
	    ;

//-----------------------------------------------------------------------------
// D e f i n i t i o n
    
		declaration
		scope {
		  boolean isTypedef;
		}
		@init {
		  $declaration::isTypedef = false;
		}// init
		  : Typedef declaration_specifiers {$declaration::isTypedef=true;} init_declarator_list SemiColon // special case, looking for typedef 
		  | declaration_specifiers init_declarator_list SemiColon
		  ;
		
			declaration_specifiers
			  : (type_qualifier)? type_specifier
			  ;
			  
			  type_qualifier
			  : Const
			  ;
			
			  type_specifier
			  : Void
			  | Char
			  | Int
			  | Float
			  | struct_or_union_specifier
			  | type_id
			  ;
			
			    type_id
			    : {isTypeName(input.LT(1).getText())}?
			      Identifier
			      //{System.out.println($Identifier.text+" is a type");}
			    ;

          struct_or_union_specifier
          options {k=3;}
            : struct_or_union Identifier? LeftBracket struct_declaration_list RightBracket
            | struct_or_union Identifier
            ;

            struct_or_union
              : Struct
              | Union
              ;

            struct_declaration_list
              : (struct_declaration)+
              ;

              struct_declaration
                : specifier_qualifier_list struct_declarator_list SemiColon
                ;

                specifier_qualifier_list
                  : ( type_qualifier | type_specifier )+
                  ;

                struct_declarator_list
                  : struct_declarator (Comma struct_declarator)*
                  ;

                  struct_declarator
                    : declarator (Colon conditional_expression)?
                    | Colon conditional_expression
                    ;

			init_declarator_list
			  : init_declarator (Comma init_declarator)*
			  ;
			
				init_declarator
				  : declarator (Assing conditional_expression)?
				  ;
			
					declarator
					  : pointer? direct_declarator
					  | pointer
					  ;
			
						pointer
						  : Asterisk (type_qualifier)+ (pointer)?
						  | Asterisk pointer
						  | Asterisk
              | LeftSquareBracket RightSquareBracket (type_qualifier)+ (pointer)?
              | LeftSquareBracket RightSquareBracket pointer
              | LeftSquareBracket RightSquareBracket
						  ;
			
						direct_declarator
						  : ( Identifier
						      {
						        if ($declaration.size()>0 && $declaration::isTypedef) {
						          $Symbols::types.add($Identifier.text);
						          System.out.println("define type "+$Identifier.text);
						        }// if
						      }
							  | LeftParenthesis declarator RightParenthesis
							  )
							  declarator_suffix
							;
			
							declarator_suffix
							  :   (LeftSquareBracket conditional_expression RightSquareBracket)*
							  ;
//-----------------------------------------------------------------------------
// E x p r e s s i o n s
expression
  : assignment_expression (Comma assignment_expression)*
  ;

	assignment_expression
	  : lvalue assignment_operator assignment_expression
	  | conditional_expression
	  ;

		lvalue
		  : unary_expression
		  ;

		assignment_operator
		  : Assing
		  | MultiplyEqual
		  | DivideEqual
		  | ModEqual
		  | AddEqual
		  | SubEqual
		  | LeftShiftEqual
		  | RightShiftEqual
		  | AndEqual
		  | ExpEqual
		  | OrEqual
		  ;

		conditional_expression
		  : logical_not_expression (Question expression Colon conditional_expression)?
		  ;

		  logical_not_expression
		    : (Not)? logical_or_expression
		    ;

				logical_or_expression
				  : logical_and_expression (Or logical_and_expression)*
				  ;
	
					logical_and_expression
					  : equality_expression (And equality_expression)*
					  ;
	
						equality_expression
						  : relational_expression ((Equal|NotEqual) relational_expression)*
						  ;
	
							relational_expression
							  : additive_expression ( ( Less | Greater | LessEqual | GreaterEqual ) additive_expression )*
							  ;
	
								additive_expression
								  : (multiplicative_expression) (Plus multiplicative_expression | Minus multiplicative_expression)*
								  ;
	
									multiplicative_expression
									  : (cast_expression) (Asterisk cast_expression | Slash cast_expression | Percent cast_expression)*
									  ;
	
										cast_expression
										  : unary_expression
										  | function_invocation
										  //| LeftParenthesis type_name RightParenthesis cast_expression
										  ;
	
											unary_expression
											  : postfix_expression
											  | unary_operator postfix_expression
											  | postfix_expression unary_operator
											  ;
	
												unary_operator
												  : Increase
												  | Decrease
												  | Plus
												  | Minus
												  ;
	
												postfix_expression
												  :   primary_expression
												        ( LeftSquareBracket expression RightSquareBracket
												        | Dot Identifier
												        )*
												  ;
	
													primary_expression
													  : Identifier
													  | constant
													  | LeftParenthesis expression RightParenthesis
													  ;
	
														constant
														    : HexLiteral
														    | OctalLiteral
														    | DecimalLiteral
														    | CharacterLiteral
														    | StringLiteral
														    | FloatingPointLiteral
														    | True
														    | False
														    ;
//-----------------------------------------------------------------------------
// S t a t e m e n t s

compound_statement
scope Symbols; // blocks have a scope of symbols
@init {
  $Symbols::types = new HashSet();
}// init
  : LeftBracket
      (declaration)* ( statement_list )?
    RightBracket
  ;

statement_list
  : ( statement )+
  ;

statement
  : labeled_statement
  | compound_statement
  | expression_statement
  | selection_statement
  | iteration_statement
  | jump_statement
  ;

	labeled_statement
	  : Case conditional_expression Colon statement
	  | Default Colon statement
	  ;

	expression_statement
	  : SemiColon
	  | expression SemiColon
	  ;

	selection_statement
	  : if_statement
	  | switch_statement
	  ;

		if_statement
		  : If LeftParenthesis expression RightParenthesis statement (options {k=1; backtrack=false;}: Else statement)?
		  ;

		switch_statement
		  : Switch LeftParenthesis expression RightParenthesis statement
		  ;

	iteration_statement
	  : while_statement
	  | do_while_statement
	  | for_statement
	  ;

		for_statement
		  : For LeftParenthesis (expression_statement|declaration) expression_statement expression? RightParenthesis statement        
		  ;

		while_statement
		  : While LeftParenthesis expression RightParenthesis statement
		  ;

		do_while_statement
		  : Do statement While LeftParenthesis expression RightParenthesis SemiColon
		  ;

	jump_statement
	  : Goto Identifier SemiColon
	  | Continue SemiColon
	  | Break SemiColon
	  | Return SemiColon
	  | Return expression SemiColon
	  ;